/*
 * Eugen Stoian <stoian.e@gmail.com> 2010
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "Surface3D.h"
#include "Curve3D.h"
#include "Parameter.h"
#include "Expression3D.h"
#include "Project.h"
Surface3D::Surface3D(QObject* parent)
    :ParametricObject3D(parent){
}

Surface3D::Surface3D(const Expression3D* expression, QObject* parent)
    :ParametricObject3D(expression, parent){
}

Surface3D::Surface3D(const QString& expressionName, QObject* parent)
    :ParametricObject3D(expressionName, parent){
}


void Surface3D::compute(){
    cleanUpPoints();
    if(!isValid())
        return;

    Parameter *u = qobject_cast<Parameter*>(autoParameters().first()->clone());
    u->reset();
    //Generare linii de coordonate u
    while(!u->isOutOfRange()){
        //cream o linie de coordonate u
        Curve3D *uLine = new Curve3D(_expression);
        uLine->setColor(_color);

        //initializam parametri curbei
        const QList<Parameter*> &params = parameters();
        QList<Parameter*>::const_iterator it = params.begin();
        while(it!=params.end()){
            uLine->updateParameter(*it);
            ++it;
        }
        //fixam parametrul u ( o copie a sa )
        Parameter *uFixed = qobject_cast<Parameter*>(u->clone());
        uFixed->setFixed(true);
        uLine->updateParameter(uFixed);
        //calculam linia u
        uLine->compute();
        //este linia vida? daca nu o pastram : daca da o stergem
        if(!uLine->isEmpty()){
            uLine->setParent(this);
            _uLines[u->value()] = uLine;
            //recalculam maxes si mins
            if(_xMin > uLine->xMin())
                _xMin =  uLine->xMin();
            if(_yMin > uLine->yMin())
                _yMin = uLine->yMin();
            if(_zMin > uLine->zMin())
                _zMin = uLine->zMin();
            if(_xMax < uLine->xMax())
                _xMax =  uLine->xMax();
            if(_yMax < uLine->yMax())
                _yMax = uLine->yMax();
            if(_zMax < uLine->zMax())
                _zMax = uLine->zMax();
        }
        else{
            delete uLine;
        }
        delete uFixed;
        ++(*u);
    }
    delete u;

    Parameter *v = qobject_cast<Parameter*>(autoParameters().last()->clone());
    v->reset();
    while(!v->isOutOfRange()){
        Curve3D *vLine = new Curve3D(_expression);
        vLine->setColor(_color);
        const QList<Parameter*> &params = parameters();
        QList<Parameter*>::const_iterator it = params.begin();
        while(it!=params.end()){
            vLine->updateParameter(*it);
            ++it;
        }
        Parameter *vFixed = qobject_cast<Parameter*>(v->clone());
        vFixed->setFixed(true);
        vLine->updateParameter(vFixed);
        vLine->compute();
        if(!vLine->isEmpty()){
            vLine->setParent(this);
            _vLines[v->value()] = vLine;
            if(_xMin > vLine->xMin())
                _xMin =  vLine->xMin();
            if(_yMin > vLine->yMin())
                _yMin = vLine->yMin();
            if(_zMin > vLine->zMin())
                _zMin = vLine->zMin();
            if(_xMax < vLine->xMax())
                _xMax =  vLine->xMax();
            if(_yMax < vLine->yMax())
                _yMax = vLine->yMax();
            if(_zMax < vLine->zMax())
                _zMax = vLine->zMax();
        }
        else{
            delete vLine;
        }
        delete vFixed;
        ++(*v);
    }
    delete v;
    reset();
}

void Surface3D::draw(){
    if(isValid()&&!isEmpty()){
        QMap<double, Curve3D* >::iterator it = _uLines.begin();
        while(it!=_uLines.end()){
            (*it)->draw();
            ++it;
        }
        it = _vLines.begin();
        while(it!=_vLines.end()){
            (*it)->draw();
            ++it;
        }
    }
}

void Surface3D::cleanUpPoints(){
    QMap<double, Curve3D*>::iterator it;
    for(it = _uLines.begin();it!=_uLines.end();++it)
        delete *it;
    for(it = _vLines.begin();it!=_vLines.end();++it)
        delete *it;
    _uLines.clear();
    _vLines.clear();
}

void Surface3D::cleanUp(){
    ParametricObject3D::cleanUp();
    cleanUpPoints();
}

bool Surface3D::isValid() const{
    if(ParametricObject3D::isValid()){
        if(autoParameters().count()==2)
            return true;
    }
    return false;
}

bool Surface3D::isEmpty() const {
    return !(_uLines.count()+_vLines.count());
}

int Surface3D::type() const {
    return Object::Surface;
}

Object *Surface3D::clone() const{
    Surface3D *surf = new Surface3D(_expression);
    return surf;
}

const QMap<double, Curve3D* > &Surface3D::uLines() const{
    return _uLines;
}

const QMap<double, Curve3D* > &Surface3D::vLines() const{
    return _vLines;
}

int Surface3D::count() const{
    return _uLines.count()+_vLines.count();
}
